package com.athuahua.webflux.controller;

import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.reactive.result.view.Rendering;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebSession;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.security.Principal;
import java.time.Duration;
import java.util.Locale;
import java.util.TimeZone;

/**
 * @Author: huahua
 * @Date: 2024/11/16 0016
 * @Time: 17:43
 * @Description:
 */

@RestController

public class HelloController {
    //webFlux 向下兼容SpringMVC的大多数注解和API
    @GetMapping("/hello")
    public String hello(@RequestParam(value = "key", required = false,
            defaultValue = "哈哈") String key) {
        return "Hello world !!! " + "key = " + key;
    }

    /**
     * 推荐方式
     * 1. 返回单个数据使用Mono Order User String Map
     * 2. 返回多个数据使用Flux Flux<Order>
     * 3. 配合Flux，完成SSE， server Send Event 服务端事件推送
     **/
    @GetMapping("/haha")
    public Mono<String> haha() {
        return Mono.just("哈哈哈");
    }

    @GetMapping("/hehe")
    public Flux<String> hehe() {
        return Flux.just("hehe1", "hehe2");
    }

    //SSE测试： 服务端推送
    //MediaType.TEXT_EVENT_STREAM_VALUE 告诉浏览器发送的是数据流
    @GetMapping(value = "/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> sse() {
        return Flux.range(1, 10)
                .map(i -> "ha-" + i)
                .delayElements(Duration.ofMillis(500));
    }

    //SSE完整写法
    @GetMapping(value = "/sseAll", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<String>> seeAll() {
        return Flux.range(1, 10)
                .map(i -> {
                    //真正构建一个SSE对象
                    ServerSentEvent<String> haha = ServerSentEvent
                            .builder("ha-" + i)
                            .id(i + "")
                            .comment("hei-" + i) //备注
                            .event("haha")
                            .build();
                    return haha;
                })
                .delayElements(Duration.ofMillis(500));
    }


    //新的API ServerWebExchange

    /**
     * 目标方法
     * ServerWebExchange	封装了请求和响应对象的对象; 自定义获取数据、自定义响应
     * ServerHttpRequest, ServerHttpResponse	请求、响应
     * WebSession	访问Session对象
     * java.security.Principal
     * org.springframework.http.HttpMethod	请求方式
     * java.util.Locale	国际化
     * java.util.TimeZone + java.time.ZoneId	时区
     *
     * @PathVariable 路径变量
     * @MatrixVariable 矩阵变量
     * @RequestParam 请求参数
     * @RequestHeader 请求头；
     * @CookieValue 获取Cookie
     * @RequestBody 获取请求体，Post、文件上传
     * HttpEntity<B>	封装后的请求对象
     * @RequestPart 获取文件上传的数据 multipart/form-data.
     * java.util.Map, org.springframework.ui.Model, and org.springframework.ui.ModelMap.	Map、Model、ModelMap
     * @ModelAttribute Errors, BindingResult	数据校验，封装错误
     * SessionStatus + class-level @SessionAttributes
     * UriComponentsBuilder	For preparing a URL relative to the current request’s host, port, scheme, and context path. See URI Links.
     * @SessionAttribute
     * @RequestAttribute 转发请求的请求域数据
     * Any other argument	所有对象都能作为参数：
     * 1、基本类型 ，等于标注@RequestParam
     * 2、对象类型，等于标注 @ModelAttribute
     */
    public String test01(@RequestParam(value = "key", required = false, defaultValue = "哈哈") String key,
                         ServerWebExchange exchange,
                         WebSession webSession,
                         Principal principal,//配合框架
                         HttpMethod method,
                         Locale locale, //国际化
                         TimeZone timeZone//时区

    ) {
        //常用
        //req resp
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        //session
        Object aaa = webSession.getAttribute("aaa");
        webSession.getAttributes().put("aaa", "nn");
        //请求方式
        String name = method.name();


        return "Hello World！" + key;
    }

    /**
     * 返回值
     *
     * @return
     * @ResponseBody 把响应数据写出去，如果是对象，可以自动转为json
     * HttpEntity<B>, ResponseEntity<B>	ResponseEntity：支持快捷自定义响应内容
     * HttpHeaders	没有响应内容，只有响应头
     * ErrorResponse	快速构建错误响应
     * ProblemDetail	SpringBoot3；
     * String	就是和以前的使用规则一样；
     * forward: 转发到一个地址
     * redirect: 重定向到一个地址
     * 配合模板引擎
     * View	直接返回视图对象
     * java.util.Map, org.springframework.ui.Model	以前一样
     * @ModelAttribute 以前一样
     * Rendering	新版的页面跳转API； 不能标注 @ResponseBody 注解
     * void	仅代表响应完成信号
     * Flux<ServerSentEvent>, Observable<ServerSentEvent>, or other reactive type	使用  text/event-stream 完成SSE效果
     * Other return values	未在上述列表的其他返回值，都会当成给页面的数据；
     */
    public ResponseEntity<String> test02() {
        return ResponseEntity.status(305)
                .header("aaa", "bbb")
                .contentType(MediaType.APPLICATION_CBOR)
                .body("bbbbb");

    }

    //Rendering 视图对象 使用@Controller 不能标注 @ResponseBody 注解
    @GetMapping("baidu")
    public Rendering render() {
        Rendering.redirectTo("/aaa"); //重定向到当前项目根路径下的aaa
        return Rendering.redirectTo("http://www.baidu.com").build();
    }

    /**
     * 文件上传额外多了两种写法
     * **/
    public String test03(
            MultipartFile file,//老版本
            FilePart filePart //新版本更快：非阻塞API
    ){
        //filePart.transferTo(); //零拷贝
        return "";
    }

}
